#pragma once
#include <utility/shared_ptr.h>

namespace shijie
{
	namespace utility
	{
		template<typename T> 
		class WeakPtr
		{	
		public:
			WeakPtr() : m_data(nullptr), m_count(nullptr) {}
			WeakPtr(const SharedPtr<T> &sp) : m_data(sp.m_data), m_count(sp.m_count) {}
			WeakPtr(const WeakPtr<T>& other) : m_data(other.m_data), m_count(other.m_count) {}
			WeakPtr(WeakPtr<T>&& other) noexcept {
				m_data = other.m_data;
				m_count = other.m_count;
				other.m_data = nullptr;
				other.m_count = nullptr;
			}
			~WeakPtr() {
				m_data = nullptr;
				m_count = nullptr;
			}

			void reset() {
				m_data = nullptr;
				m_count = nullptr;
			}

			bool expired() const {
				return m_count == nullptr || *m_count <= 0;
			}

			SharedPtr<T> lock() const {
				if (expired()) {
					return SharedPtr<T>();
				}
				SharedPtr<T> sp;
				sp.m_data = m_data;
				sp.m_count = m_count;
				if (m_count != nullptr) {
					(*m_count)++;
				}
				return sp;
			}

			void swap(WeakPtr<T>& other) noexcept {
				auto data = other.m_data;
				auto count = other.m_count;
				other.m_data = m_data;
				other.m_count = m_count;
				m_data = data;
				m_count = count;
			}

			int use_count() const {
				return (m_count != nullptr && m_data != nullptr) ? *m_count : 0;
			}

			WeakPtr &operator = (const SharedPtr<T>& sp) {
				m_data = sp.m_data;
				m_count = sp.m_count;
				return *this;
			}

			WeakPtr &operator = (const WeakPtr<T>& other) {
				if (this == &other) {
					return *this;
				}
				m_data = other.m_data;
				m_count = other.m_count;
				return *this;
			}

			WeakPtr &operator = (WeakPtr<T>&& other) noexcept {
				if (this == &other) {
					return *this;
				}
				m_data = other.m_data;
				m_count = other.m_count;
				other.m_data = nullptr;
				other.m_count = nullptr;
				return *this;
			} 

		private:
			T * m_data;
			int * m_count;
		};
	}
}